package DBLag;

import ModelLag.Employee;
import ModelLag.Shift;
import ModelLag.WorkingPlan;
import java.sql.*;
import java.util.ArrayList;
import java.util.GregorianCalendar;

/**
 * Databaseklasse for Shift som står for kommunikationen til databasen og som står
 * for opbygningen af Shift objekter som skal bruges i de højere lag.
 * @author DM75 Gruppe 3
 * @version 2011-12-22
 */
public class DbShift implements IFDbShift
{
    // Forbindelsen til databasen.
    private Connection con;
    // En instans variabel af typen PreparedStatement som indeholder et PreparedStatement
    // til brug ved findmetode.
    private PreparedStatement pStmtSelect;
    private PreparedStatement pStmtDateLocation;
    // Slå print fra/til.
    private boolean debug = false;

    public DbShift() 
    {
        con = DbConnection1.getInstance().getDBcon();
        pStmtSelect = null;
        pStmtDateLocation = null;
    }
    
    /**
     * Henter det højeste ShiftID i Shift tabellen i databasen og ligger 1 til for
     * at lave et nyt shiftID til en ny række.
     * @return ShiftID til ny række.
     */
    private int getNextShiftID()
    {
        String query = null;
        ResultSet results = null;
        int nextInt =0;
        try
        {
            query = "SELECT MAX(ShiftID) FROM Shift";
            PreparedStatement preStmt = con.prepareStatement(query);
            results = preStmt.executeQuery();
        
        if(results.next())
        {
            nextInt = results.getInt(1);
            nextInt++;
        }
        preStmt.close();
        
        return nextInt; 
        
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
            return -1;
        }
    }
    
    // pStmtSelect må ikke lukkes(.close) i findShift(), da man bruger det hver gang man skal finde et Shift objekt.
    /**
     * Finder en shift i databasen og sørger for at det bliver bygget op til et 
     * objekt ved hjælp af buildShift() og cprnummer på medarbejder, dato, og lokation.
     * @param cprNo CPR-Nummer på medarbejderen. Skal anføres DDMMYY-XXXX.
     * @param date GregorianCalendar objekt for den ønskede dag.
     * @param location Afdeling i virksomheden.
     * @param retriveAssociation true = associering laves til Employee og Workingplan.
     * @return Hvis fundet returneres et Shift objekt ellers null.
     */
    public Shift findShift(String cprNo, String startTime, String endTime, GregorianCalendar date, String location, boolean retriveAssociation)
    { 
        if(pStmtSelect == null)
        {
            pStmtSelect = getPreparedStmt();
        }
        Shift shiftObj = null;
        ResultSet results = null;
        
        try
        {
            pStmtSelect.setString(1, cprNo);
            
            // Konvertering fra Java til SQL.
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            pStmtSelect.setDate(2, tempDate);
            pStmtSelect.setString(3, location);
            pStmtSelect.setString(4, startTime);
            pStmtSelect.setString(5, endTime);
            
            results = pStmtSelect.executeQuery();
            if(results.next())
            {
                shiftObj = buildShift(results);   
            }
            
            if(retriveAssociation)
                {
                    IFDbEmployee dbEmp = new DbEmployee();
                    String cprNo1 = shiftObj.getEmployee().getCprNo();
                    Employee employeeObj = dbEmp.findEmployee(cprNo1);
                    shiftObj.setEmployee(employeeObj);
                    //UDKOMMENTERET fordi der ikke er grund til at et shift objekt kender til sin WP, med det GUI der er lavet pt.
//                    IFDbWorkingPlan dbWorkingPlan = new DbWorkingPlan();
//                    String location1 = shiftObj.getWorkingPlan().getLocation();
                    
                    // Konvertering fra SQL til Java.
                    //UDKOMMENTERET fordi der ikke er grund til at et shift objekt kender til sin WP, med det GUI der er lavet pt.
//                    GregorianCalendar date1 = new GregorianCalendar();
//                    date1.setTimeInMillis(shiftObj.getWorkingPlan().getDate().getTimeInMillis());                   
//                    WorkingPlan workingPlanObj = dbWorkingPlan.findWorkingPlan(date1, location1, true);
//                    shiftObj.setWorkingPlan(workingPlanObj);
                }
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return shiftObj; 
    }
    /**
     * Tilføjer alle Shifts med denne dato og location ind i en ArrayList.
     * @param date
     * @param location
     * @param retriveAssociation
     * @return ArrayList med Shift objekter.
     */
    public ArrayList<Shift> getShiftsWithThisDateLocation(GregorianCalendar date, String location, boolean retriveAssociation)
    {
        ArrayList<Shift> list = new ArrayList<Shift>();
        if(pStmtDateLocation == null)
        {
            pStmtDateLocation = getPreparedStmtDateLocation();
        }
        
        Shift shiftObj = null;
        ResultSet results = null;
        
        try
        {
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
            pStmtDateLocation.setDate(1, tempDate);
            pStmtDateLocation.setString(2, location);
            
            results = pStmtDateLocation.executeQuery();
            while(results.next())
            {
                
                shiftObj = buildShift(results);
                
                if(retriveAssociation)
                {
                    IFDbEmployee dbEmp = new DbEmployee();
                    String cprNo1 = shiftObj.getEmployee().getCprNo();
                    Employee employeeObj = dbEmp.findEmployee(cprNo1);
                    shiftObj.setEmployee(employeeObj);
                    //UDKOMMENTERET fordi der ikke er grund til at et shift objekt kender til sin WP, med det GUI der er lavet pt.
//                    IFDbWorkingPlan dbWorkingPlan = new DbWorkingPlan();
//                    String location1 = shiftObj.getWorkingPlan().getLocation();
                    
                    //UDKOMMENTERET fordi der ikke er grund til at et shift objekt kender til sin WP, med det GUI der er lavet pt.
                    // Konvertering fra SQL til Java.
//                    GregorianCalendar date1 = new GregorianCalendar();
//                    date1.setTimeInMillis(shiftObj.getWorkingPlan().getDate().getTimeInMillis());
                    
                    // false fordi der ikke ønskes at få associationer til workingPlanObjs shifts i dens arrayliste.
//                    WorkingPlan workingPlanObj = dbWorkingPlan.findWorkingPlan(date1, location1, false);
//                    shiftObj.setWorkingPlan(workingPlanObj);
                }
                list.add(shiftObj);   
            } 
            
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return list;
    }
   
    /** BRUGES IKKE. KAN MÅSKE SLETTES. AFVENT!!
     * Henter alle rækker i Shift tabellen i databasen og bygger dem op til objekter ved hjælp af
     * buildShift() og derefter tilføjes hvert objekt til en ArrayList.
     * @return ArrayList med alle shifts som findes i Shift tabellen i databasen.
     */
    public ArrayList<Shift> getAllShifts()
    {
        ArrayList<Shift> list = new ArrayList<Shift>();
        PreparedStatement pstmt = null;
        
        String allShifts = "SELECT startTime, endTime, hours, cprNo, date, location "
                         + "FROM Shift, WorkingPlan "
                         + "WHERE Shift.workingPlanID = WorkingPlan.workingPlanID";
        if(debug)
        {
            System.out.println(allShifts);
        }
        
        Shift shiftObj = null;
        ResultSet results = null;
        
        try
        {
            pstmt = con.prepareStatement(allShifts);
            pstmt.setQueryTimeout(5);
            results = pstmt.executeQuery();
            while(results.next())
            {
                shiftObj = buildShift(results);
                list.add(shiftObj);
            }
            pstmt.close();
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        
        return list;
    }    
    
    /**
     * Indsætter et Shift objekt som en række i Shift tabellen i databasen.
     * @param shiftObj Det objekt som ønskes indsat i Shift tabellen i databasen.
     * @param date Objekt af den dato shift skal indsættes med.
     * @param location Afdeling som shift skal indsættes med.
     * @return -1 = mislykkedes. 1 = lykkedes.
     */
    public int insertShift(Shift shiftObj, GregorianCalendar date, 
                           String location)
    {
        IFDbWorkingPlan dbWorkingP = new DbWorkingPlan();     
        int workingPlanID = dbWorkingP.getWorkingPlanID(date, location);
        if(workingPlanID != -1)
        {
            int rc = -1;
            PreparedStatement pstmt = null;
            String insert = "INSERT INTO Shift (shiftID, startTime, endTime, "
                          + "hours, cprNo, workingPlanID) "
                          + "VALUES (?,?,?,?,?,?)";
            if(debug)
            {
                System.out.println(insert);
            }
            if(getNextShiftID() != -1)
            {
                try
                {
                    pstmt = con.prepareStatement(insert);
                    pstmt.setQueryTimeout(5);
                    pstmt.setInt(1, getNextShiftID());
                    pstmt.setString(2, shiftObj.getStartTime());
                    pstmt.setString(3, shiftObj.getEndTime());
                    pstmt.setDouble(4, shiftObj.getHours());
                    pstmt.setString(5, shiftObj.getEmployee().getCprNo());
                    pstmt.setInt(6, workingPlanID);
                    rc = pstmt.executeUpdate();
                    pstmt.close();
                }
                catch(SQLException sqlE)
                {
                    System.out.println("SQL Error");
                    System.out.println(sqlE.getMessage());
                }
                catch(Exception e)
                {
                    System.out.println(e.getMessage());
                }
                return rc;
            }
            else
            {
                System.out.println("FEJL i getNextShiftID()");
                return rc;
            }
        }
        else
        {
            return workingPlanID;
        }
    }
    
    /**
     * Sletter rækken i Shift tabellen i databasen ved hjælp af parameterne.
     * @param cprNo CPR-Nummer på medarbejderen. DDMMYY-XXXX.
     * @param startTime "HH:MM"
     * @param endTime "HH:MM"
     * @param date Objekt med den dato hvor shift objektet findes.
     * @param location Afdeling hvor shift objektet findes.
     * @return -1 = mislykkedes. 1 = lykkedes.
     */
    public int deleteShift(String cprNo, String startTime, String endTime, GregorianCalendar date, String location)
    {
        int rc = -1;
        PreparedStatement pstmt = null;
        
        java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());
        //Der sker en subselect da der skal slettes efter shiftID. derfor starter den i den inderste subselect.
        String delete = "DELETE FROM Shift "
                       + "WHERE shiftID = (SELECT Shift.shiftID "
                                        + "FROM Shift, WorkingPlan "
                                        + "WHERE date = ? AND location = ? AND cprNo = ? AND startTime = ? AND endTime = ? AND Shift.workingPlanID = WorkingPlan.workingPlanID)";
        if(debug)
        {
            System.out.println(delete);
        }
        
        try
        {
            pstmt = con.prepareStatement(delete);
            pstmt.setQueryTimeout(5);
            pstmt.setDate(1, tempDate);
            pstmt.setString(2, location);
            pstmt.setString(3, cprNo);
            pstmt.setString(4, startTime);
            pstmt.setString(5, endTime);
            rc = pstmt.executeUpdate();
            
            pstmt.close();
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }

        return rc;
    }
    
    /**
     * Opdaterer rækken i Shift tabellen i databasen i forhold til shift objektet.
     * @param shiftObj Shift objekt som er det der skal opdateres.
     * @param date Objekt med dato i for den pågældende dag.
     * @param location Afdelingen for shift objektet.
     * @param cprNo CPR-NUMMER for medarbejderen. DDMMYY-XXXX.
     * @param newStartTime Ny start tid for shift objektet "HH:MM".
     * @param newEndtime Ny slut tid for shift objektet "HH:MM".
     * @param newHours Nyt antal timer for shift objektet.
     * @return -1 = mislykkedes. 1 = lykkedes.
     */
    public int updateShift(Shift shiftObj, GregorianCalendar date, String location, String newCprNo, String newStartTime, String newEndtime, Double newHours)
    {
        int rc = -1;
        
        try
        {   
            String query = "UPDATE Shift "
                         + "SET startTime = ?, endTime = ?, hours = ?, cprNo = ? "
                         + "WHERE shiftID = (SELECT Shift.shiftID "
                                          + "FROM Shift, WorkingPlan "
                                          + "WHERE date = ? AND location = ? AND cprNo = ? AND startTime = ? AND endTime = ? AND Shift.workingPlanID = WorkingPlan.workingPlanID)";
            
            PreparedStatement pstmt = con.prepareStatement(query);
            pstmt.setQueryTimeout(5);
            
            if(debug)
            {
                System.out.println(query);
            }
            
            java.sql.Date tempDate = new java.sql.Date(date.getTimeInMillis());

            pstmt.setString(1, newStartTime);
            pstmt.setString(2, newEndtime);
            pstmt.setDouble(3, newHours);
            pstmt.setString(4, newCprNo);
            pstmt.setDate(5, tempDate);
            pstmt.setString(6, location);
            pstmt.setString(7, shiftObj.getEmployee().getCprNo());
            pstmt.setString(8, shiftObj.getStartTime());
            pstmt.setString(9, shiftObj.getEndTime());
            
            rc = pstmt.executeUpdate();
            
            pstmt.close();
        }
        
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }

        return rc;
    } 
    
    /**
     * Opbygning af Shift objekt sker i denne metode.
     * @param results ResultSet. En form for tabel med resultaterne i fra det
     * preparedstatement som er sendt til databasen.
     * @return Shift objekt.
     */
    private Shift buildShift(ResultSet results)
    {
        Shift shiftObj = new Shift();
        Employee empObj = new Employee();
        WorkingPlan workingPlanObj = new WorkingPlan();
        shiftObj.setEmployee(empObj); // lav reference til Employee
        shiftObj.setWorkingPlan(workingPlanObj); // lav reference til Workingplan
        GregorianCalendar newCal = new GregorianCalendar();
        try
        {
            empObj.setCprNo(results.getString("cprNo"));
            
            newCal.setTimeInMillis(results.getDate("date").getTime());
            workingPlanObj.setDate(newCal);
            workingPlanObj.setLocation(results.getString("location"));

            shiftObj.setStartTime(results.getString("startTime"));
            shiftObj.setEndTime(results.getString("endTime"));
            shiftObj.setHours(results.getDouble("hours"));
        }
        catch(Exception e)
        {
            System.out.println("Error in buildning shift object");
            System.out.println(e.getMessage());
        }
        return shiftObj;
    }
    
    /**
     * PreparedStatement som bruges til findmetoden. Dette laves en gang og gemmes
     * derefter i en instans variabel, da dette preparedStatement bruges ofte kan
     * dette betale sig at gemme og derved går det hurigtere end hvis man skal til at
     * lave et nyt hver gang.
     * @return PreparedStatement objekt.
     */
    private PreparedStatement getPreparedStmt()
    {
        try
        {         
            String query = "SELECT startTime, endTime, hours, cprNo, date, location "
                         + "FROM Shift, WorkingPlan "
                         + "WHERE cprNo = ? AND date = ? AND location = ? AND startTime = ? AND endTime = ? AND Shift.workingPlanID = WorkingPlan.workingPlanID";
           
            if(debug)
            {
                System.out.println("Preparestatement " + query);
            }
            pStmtSelect = con.prepareStatement(query);
            pStmtSelect.setQueryTimeout(5);
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return pStmtSelect;
    }
    
        /**
     * PreparedStatement som bruges til findmetoden ud fra date og location. Dette laves en gang og gemmes
     * derefter i en instans variabel, da dette preparedStatement bruges ofte kan
     * dette betale sig at gemme og derved går det hurigtere end hvis man skal til at
     * lave et nyt hver gang.
     * @return PreparedStatement objekt.
     */
    private PreparedStatement getPreparedStmtDateLocation()
    {
        try
        {         
            String query = "SELECT date, location, cprNo, startTime, endTime, hours "
                         + "FROM WorkingPlan, Shift "
                         + "WHERE date = ? AND location = ? AND WorkingPlan.workingPlanID = Shift.workingPlanID";            
            if(debug)
            {
                System.out.println("pStmtDateLocation " + query);
            }
            
            pStmtDateLocation = con.prepareStatement(query);
            pStmtDateLocation.setQueryTimeout(5);
        }
        catch(SQLException sqlE)
        {
            System.out.println("SQL Error");
            System.out.println(sqlE.getMessage());
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        return pStmtDateLocation;
    }
}
